
// including external libraries
#include "Material.hpp"
#include <iostream>
#include <cstdlib>
#include "math.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_projection.hpp>
#include <glm/gtc/matrix_transform.hpp>

// including internal headers
#include "Camera.hpp"
#include "Shader.h"
#include "utils.hpp"
#include "Entity.hpp"
#include "Node.hpp"
#include "GL/glext.h"
#include "tinyxml.h"

#ifndef __PARTICLE_HPP__
#define __PARTICLE_HPP__


using namespace std;
using namespace sf;

#define INITIAL_SPEED_ARRAY 0
#define START_TIME_ARRAY 1
#define DIRECTION_ARRAY 2

#define INITIAL_SPEED_ATTR 1
#define START_TIME_ATTR 2
#define DIRECTION_ATTR 3

class ParticleEmitter {
	protected:
		list<ParticleEmitter*>::iterator m_iterator;

		static int id;
		int m_id;
		glm::vec3 m_direction;
		glm::vec3 m_position;
		float m_speedIntensity;
		float m_time;
		float m_accTime;
		float m_pointSize;
		float m_sizeUpdate;
		float m_alphaDecrease;
		float m_intensity;

		float m_totalExistenceTime;
		int m_lastLaunched;
		// uniform variable identifier for shader
		int m_globalTime_id, m_initialPosition_id, m_cameraPosition_id, m_alphaDecrease_id;
		int m_sizeUpdate_id, m_initialPointSize_id;
		int m_MVPMatrix_id;
		GLuint* m_buffers;
		Camera* m_camera;
		int m_limits;
		int pack; // nombre de particule lancé par seconde
		Material* m_material;
		GLfloat* direction;
		GLfloat* initialSpeed;
		GLfloat* startTime;
	public:
		/** particle constructor */
		ParticleEmitter(float pointSize_, float sizeUpdate_, float alphaDecrease_, Material* material_, float intensity_, float tet_, int limits_, int pack_);

		/** destructor : delete assigned openGL buffers */
		~ParticleEmitter() {
			glDeleteBuffers(3, m_buffers);
		}

		void setPosition(glm::vec3 position) { m_position = position;};
		void setCamera(Camera* camera) { m_camera = camera;};
		void setDirection(glm::vec3 direction) { m_direction = direction;};
		void setSpeed(float speed) { m_speedIntensity = speed;};
		void render();
		virtual void createBuffer();
		void reinit() { m_lastLaunched = 0; m_time = m_accTime = 0.0f; createBuffer();};

		/** setting ParticleEmitter position on Particle 
		  *	Gestionner list */
		void setIterator(list<ParticleEmitter*>::iterator it_) { m_iterator = it_;};

		/** retrieving Particle Emitter iterator */
		list<ParticleEmitter*>::iterator getIterator() { return m_iterator; };

		/** updating particle emitter 
	      * return false if particle emitter has to be deleted, true elsewhere */
		virtual bool update(float);
};

class ParticleProjectileEmitter : public ParticleEmitter {
	public:
		ParticleProjectileEmitter(float pointSize_, float sizeUpdate_, float alphaDecrease_, Material* material_, float intensity_, float tet_, int limits_, int pack_) : ParticleEmitter(pointSize_, sizeUpdate_, alphaDecrease_, material_, intensity_, tet_, limits_, pack_) {
			// initializeing ParticleProjectileEmitter as a ParticleEmitter
		};

		virtual void createBuffer();
};

class ParticleSetting {
	public:
		float m_pointSize, m_sizeUpdate, m_alphaDecrease, m_intensity, m_totalExistenceTime;
		int m_limits, m_pack;
		Material* m_material; 
		string m_particleSubType;
};


class ParticleGestionner {
	protected:
		/** list of managed particle emitter */
		static list<ParticleEmitter*> m_particleEmitterList;

		/** map of Particle Emitter parameters */
		static map<string, ParticleSetting*> m_particleSetting;
	public:
		static void attachParticleEmitter(ParticleEmitter*);

		/** detaching and deleting the given particle emitter */
		static list<ParticleEmitter*>::iterator detachParticleEmitter(ParticleEmitter*);
		
		/** updating all attached particle emitter */
		static void update(float time);

		/** rendering particle on screen */
		static void render();

		/** creating (new) and attaching a particle emitter,
		  * getting its parameters from the script particleName */
		static ParticleEmitter* createParticleEmitter(string particleName);

		/** load particle script file */
		static void loadParticleScript(string filename_);
};



#endif
